Skip to content

feat(ui): improve cross-platform server diagnostics - #643

Open
pascalandr wants to merge 5 commits into
NeuralNomadsAI:devfrom
pascalandr:feat/issue-130-connectivity-diagnostics
Open

feat(ui): improve cross-platform server diagnostics#643
pascalandr wants to merge 5 commits into
NeuralNomadsAI:devfrom
pascalandr:feat/issue-130-connectivity-diagnostics

Conversation

@pascalandr

Copy link
Copy Markdown
Contributor

Summary

  • add a shared CodeNomad server connectivity card to Settings → Info
  • show effective listening mode, bind host, listener URLs, workspace root, and candidate network addresses
  • clarify that per-workspace PID and port values describe the OpenCode process
  • extend copied/downloaded diagnostics with listener details and a sharing privacy notice
  • allow metadata refresh to recover after a failed request

Scope

This deliberately keeps server configuration read-only. It improves discoverability and troubleshooting in Electron, Tauri, and remote web sessions without exposing raw CLI arguments or weakening desktop listener/authentication invariants.

The Remote Access shortcut is shown only in a local desktop window; all diagnostic details come from the existing cross-platform /api/meta contract.

Validation

  • npm run typecheck
  • npm run build --workspace @codenomad/ui
  • node --import tsx --test packages/ui/src/components/settings/info-settings-diagnostics.test.ts packages/ui/src/lib/server-meta.test.ts
  • locale parity checked across all nine supported locales
  • git diff --check

Closes #130

Add a shared Settings Info card for the effective CodeNomad listener mode, bind host, local and remote URLs, workspace root, and candidate network addresses. Keep the Remote Access shortcut capability-gated so the same view remains useful in Electron, Tauri, and remote web sessions without exposing mutable server arguments.

Clarify that per-workspace PID and port details belong to the OpenCode process, extend downloadable diagnostics with connectivity data and a privacy notice, and use the existing clipboard fallback for insecure remote contexts. Failed metadata requests now release the shared pending request so refresh can recover.

Add focused formatter and retry regression tests, run them in PR validation, and translate all new labels across the nine supported locales.

@pascalandr pascalandr left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatekeeper review — FAIL

  1. Medium: specific-interface bindings produce false diagnostics
    packages/ui/src/components/settings/info-settings-section.tsx:251-255,279-294
    packages/ui/src/components/settings/info-settings-diagnostics.ts:25-36
    With valid --host 192.168.1.20, the card reports “All network interfaces” and includes https://127.0.0.1:<remotePort> as a candidate. The HTTPS listener is bound only to 192.168.1.20, so that loopback URL is unreachable. This misstates exposure and undermines troubleshooting.
    Minimal correction: derive a “Specific interface” presentation for concrete non-loopback hosts and exclude loopback candidates in that mode from both UI and exported reports. Add a concrete-host regression test.

  2. Medium: insecure-context copy loses keyboard focus
    packages/ui/src/components/settings/info-settings-section.tsx:170-175
    packages/ui/src/lib/clipboard.ts:35-48
    Remote HTTP sessions now use the shared fallback, which focuses a temporary textarea and removes it without restoring focus. Successful copying leaves keyboard and screen-reader users at the document body; exceptions can leave the temporary element behind.
    Minimal correction: preserve and restore the active element, make the textarea read-only, and remove it in finally.

Validation: focused tests passed (3/3), UI typecheck and build passed, git diff --check passed, locale key parity was verified, and the PR’s Linux/Windows test jobs passed.

@github-actions

Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/31385174375

Artifacts expire in 7 days.
Artifacts:

  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-tauri-macos
  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-tauri-windows
  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-tauri-macos-arm64
  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-electron-macos
  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-tauri-linux
  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-electron-linux
  • pr-643-7a86bc178f794cb31ae92c5831e8f021b32188d3-electron-windows

Distinguish concrete bind hosts from wildcard all-interface listeners and remove unreachable loopback candidates from both the connectivity card and exported diagnostics. Add the specific-interface label to every supported locale and cover the concrete-host case.

Harden the shared insecure-context clipboard fallback by keeping its temporary textarea read-only, cleaning it up on every exit path, and restoring keyboard focus. Add focused success and failure cleanup tests to PR validation.

Validated with root typechecks, the UI production build, six focused tests, locale parity checks, and git diff --check.

@pascalandr pascalandr left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatekeeper re-review — FAIL

  1. Medium: concrete loopback binds still advertise unreachable listener candidates.
    packages/ui/src/components/settings/info-settings-diagnostics.ts:11-19
    packages/ui/src/components/settings/info-settings-section.tsx:252-295
    packages/ui/src/components/settings/info-settings-diagnostics.test.ts:56-67
    Filtering only applies when the derived mode is specific. A valid --host 127.0.0.2 remains local, preserving both 127.0.0.1 and 127.0.0.2 candidates. A Windows socket probe confirmed that a listener bound to 127.0.0.2 was unreachable through 127.0.0.1. The card and exported report therefore still expose an unreachable loopback URL.
    Minimal correction: classify wildcard versus concrete binding independently of the display mode, retain only addresses compatible with a concrete bind host, and add non-default IPv4 and IPv6 loopback tests.

  2. Medium: valid IPv6 bind hosts produce incorrect diagnostics.
    packages/ui/src/components/settings/info-settings-diagnostics.ts:11-19,38-50
    packages/ui/src/components/settings/info-settings-section.tsx:252-295
    packages/server/src/index.ts:553-591
    packages/server/src/server/network-addresses.ts:20-60
    Raw IPv6 hosts are interpolated without URL brackets, producing malformed values such as https://:::9898; IPv6 interfaces are not included in candidates; and the expanded wildcard 0:0:0:0:0:0:0:0 is labeled “Specific network interface,” understating exposure.
    Minimal correction: normalize wildcard forms, bracket IPv6 literals when constructing URLs, resolve matching IPv6 candidates, and cover ::, ::1, expanded wildcard, and concrete IPv6 hosts.

The clipboard fallback now restores focus and removes its textarea on success, false returns, and exceptions. Metadata retry/deduplication, privacy messaging, accessibility behavior, nine-locale key parity, test isolation, and CI wiring showed no additional actionable defects.

Validation: complete 27-file PR diff and surrounding listener code reviewed; 6 focused tests passed; all 176 CI-wired UI tests and UI typecheck passed; 1,577-key locale parity verified; Linux and Windows test jobs passed; git diff --check passed; worktree remained unchanged.

Centralize wildcard, loopback, and URL-host handling so concrete loopback bindings advertise only the address that is actually listening. Preserve exact loopback hosts for plugin URLs and make HTTPS respect the configured host rather than silently rebinding to 127.0.0.1.

Add IPv6 interface enumeration and bracketed URL generation for compact, expanded wildcard, loopback, and concrete addresses. Generated certificates now treat IPv6 values as IP SANs, while metadata, CORS, and UI diagnostics share consistent listener semantics.

Cover host classification, concrete candidates, IPv6 wildcard enumeration, plugin URLs, certificate SANs, and defensive UI filtering. Server/UI typechecks, the UI build, 26 focused tests, and git diff checks pass; the full local server suite has one unrelated default-branch assertion failure in git-worktrees.test.ts.

@pascalandr pascalandr left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatekeeper re-review — FAIL

  1. High: IPv6 wildcard HTTPS advertises a plugin URL rejected by its generated certificate.
    packages/server/src/server/listener-base-url.ts:16-23
    packages/server/src/server/tls.ts:70-79,233-247
    packages/server/src/workspaces/manager.ts:402-423
    Default HTTPS-only --host :: publishes https://[::1]:<port> to OpenCode, but the generated certificate contains only DNS:localhost and IP:127.0.0.1. TLS verification fails, breaking plugin-to-server communication. Existing generated certificates are also reused until expiration, so corrected SAN requirements would not reach upgrades.
    Minimal correction: include ::1 for IPv6 wildcard certificates and rotate the leaf certificate whenever required host/configured SANs are absent. Cover fresh and reused configuration directories.

  2. Medium: IPv6 wildcard diagnostics omit reachable IPv4 addresses.
    packages/server/src/server/http-server.ts:345-348
    packages/server/src/server/network-addresses.ts:32-40,56-67
    Node binds :: as dual-stack by default on supported platforms, but enumeration retains only IPv6 interfaces. The server therefore accepts IPv4 connections while metadata and diagnostics omit those addresses, understating exposure and hiding usable URLs. A Windows probe reached the :: listener through 127.0.0.1.
    Minimal correction: explicitly define the listener’s ipv6Only behavior and enumerate both families when dual-stack is enabled.

  3. Medium: IPv6 link-local candidates are unusable because their scope IDs are discarded.
    packages/server/src/server/network-addresses.ts:34-40,61-66
    fe80::/10 interfaces are classified as external and can become the primary remote URL, but their scopeid is omitted. The resulting URLs cannot reliably identify an interface. This Windows host produced four such advertised URLs.
    Minimal correction: exclude IPv6 link-local addresses from URL candidates unless a supported zone-aware representation is implemented.

  4. Medium: valid zero-padded IPv6 loopback forms are misclassified.
    packages/server/src/server/network-host.ts:14-25
    packages/server/src/server/http-server.ts:180-183
    packages/server/src/index.ts:580
    Valid ::0001 and 0:0:0:0:0:0:0:0001 addresses return false from isLoopbackHost. Diagnostics label them as specific interfaces and CORS treats their loopback listener as remotely bound, allowing arbitrary origins.
    Minimal correction: recognize a final IPv6 segment matching ^0*1$ and add both forms to host, metadata, and CORS tests.

  5. Low: newly exported connectivity diagnostics bypass i18n.
    packages/ui/src/components/settings/info-settings-diagnostics.ts:42-62
    packages/ui/src/components/settings/info-settings-section.tsx:170-180
    Newly added labels, modes, scopes, and fallback values are always English, despite the localized privacy warning asking users to inspect the report before sharing it.
    Minimal correction: supply translated labels and values to buildDiagnosticReport, reusing the connectivity message keys.

Clipboard cleanup/focus restoration, metadata retry, concrete IPv4 candidates, standard compact/expanded IPv6 URLs, listener host binding, accessibility, privacy disclosure, locale parity, and focused test isolation otherwise behaved correctly.

Validation: all three commits reviewed against GitHub base 67cb394e; 18 focused server tests and 8 focused UI tests passed; server/UI typechecks and UI production build passed; direct Windows TLS, dual-stack, link-local, and padded-loopback probes reproduced the findings; git diff --check passed. The full server suite had only the specified unrelated git-worktrees.test.ts assertion failure. GitHub Linux tests pass; the current Windows failure is in an unchanged Tauri cross-host test and is not PR-caused.

Make IPv6 wildcard listeners explicitly dual-stack, omit unusable link-local candidates, and recognize zero-padded loopback forms so reported addresses match effective reachability.

Regenerate generated TLS certificates when the active host or configured SANs are missing, including IPv6 loopback coverage for wildcard listeners. Localize every exported diagnostic report label and add focused regression coverage.

Validated with focused server and UI tests, server/root typechecks, the UI production build, and git diff checks.

@pascalandr pascalandr left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatekeeper review - FAIL

  1. Medium: IPv4-mapped IPv6 hosts are misclassified.
    packages/server/src/server/network-host.ts:8-25
    packages/server/src/server/network-addresses.ts:32-45
    packages/server/src/index.ts:553-585

    Canonical forms such as ::ffff:0:0 and ::ffff:7f00:1 bind as IPv4 wildcard and loopback addresses, but the helpers classify them as concrete external IPv6 hosts. Diagnostics can understate wildcard exposure, label loopback as external, and advertise an unusable wildcard URL. Normalize IPv4-mapped addresses before classification and add canonical-form coverage.

  2. Medium: certificate reuse repeatedly rotates certificates for valid host inputs.
    packages/server/src/server/tls.ts:255-291
    packages/server/src/server/network-host.ts:28-30

    Non-ASCII DNS hosts such as münchen.local and scoped IPv6 hosts such as fe80::1%12 regenerate the generated leaf certificate on every startup. Normalize DNS values with domainToASCII() and reject or explicitly normalize zone-scoped IPv6 hosts before URL and SAN generation.

Validation: 29 focused tests, root/server typechecks, nine-locale parity, and git diff --check passed. Linux CI passed. Windows CI failed in an unchanged Tauri cross-host election test; build and artifact publication were consequently skipped/failed. The refocused #130 diagnostics scope is otherwise fulfilled.

Canonicalize IPv4-mapped IPv6 bind addresses before listener classification so wildcard exposure, loopback scope, plugin URLs, and diagnostics reflect the effective IPv4 endpoint.

Convert internationalized DNS names to ASCII for stable generated certificate reuse. Reject zone-scoped IPv6 CLI hosts because they cannot form supported listener URLs, while normalizing scoped SAN entries to their certificate-safe address.

Covered mapped wildcard and loopback forms, IDN certificate reuse, scoped SANs, and CLI host validation with focused tests. Server/root typechecks and diff checks pass.

@pascalandr pascalandr left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatekeeper re-review - PASS

No actionable findings.

The previously reported findings are resolved, including IPv4-mapped IPv6 classification, IDN and scoped-IPv6 certificate reuse, dual-stack diagnostics, required-SAN certificate rotation, localization, privacy, accessibility, and issue #130 scope.

Validation covered the complete diff from 67cb394e through e8e2b5cb. Typechecks, the UI build, focused tests, and git diff --check passed. GitHub Linux and Windows/Tauri test jobs pass; build and artifact jobs are still running with no failures currently reported.

@github-actions

Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/31391698012

Artifacts expire in 7 days.
Artifacts:

  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-tauri-macos
  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-tauri-windows
  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-electron-macos
  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-tauri-macos-arm64
  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-tauri-linux
  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-electron-linux
  • pr-643-e8e2b5cbff76dae3ba6353be156089ee67bbd639-electron-windows

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[QOL] Improve cross-platform CodeNomad server and connectivity diagnostics

1 participant